package com.swfgui.loader
{
	import com.swfgui.debug.Logger;
	import com.swfgui.utils.time.STimer;
	
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	[Event(name="progress", type="com.swfgui.loader.SLoaderEvent")]

	[Event(name="complete", type="com.swfgui.loader.SLoaderEvent")]

	[Event(name="error", type="com.swfgui.loader.SLoaderEvent")]

	/**
	 * 方便的资源加载类，可顺序加载多个资源
	 * @author llj
	 */
	public class SLoader extends EventDispatcher
	{
		private static var _fileTypes:Dictionary;

		/**
		 * [文件扩展名] = SLoaderDataFormat.***
		 * @default
		 */
		public static function get fileTypes():Dictionary
		{
			if (!_fileTypes)
			{
				_fileTypes = new Dictionary();
				_fileTypes["swf"] = SLoaderDataFormat.DISPLAY;
				_fileTypes["png"] = SLoaderDataFormat.DISPLAY;
				_fileTypes["jpg"] = SLoaderDataFormat.DISPLAY;
				_fileTypes["gif"] = SLoaderDataFormat.DISPLAY;
				_fileTypes["xml"] = SLoaderDataFormat.TEXT;
				_fileTypes["js"] = SLoaderDataFormat.TEXT;
				_fileTypes["txt"] = SLoaderDataFormat.TEXT;
				_fileTypes["csv"] = SLoaderDataFormat.TEXT;
				_fileTypes["cfg"] = SLoaderDataFormat.BINARY;
			}

			return _fileTypes;
		}

		/**
		 * 供用户临时存储数据用的
		 * @default
		 */
		public var userData:*;

		/**
		 * 资源的版本列表，放置浏览器缓存
		 * @default
		 */
		public static var revisionList:Dictionary;

		public var baseUrl:String = null;

		private var _useCurrentDomain:Boolean = false;
		private var _checkPolicyFile:Boolean;

		private var _curLoadIndex:int;
		private var _isLoadComple:Boolean;

		private var _loaderList:Dictionary;

		private var params:Array;
		private var onComplete:Function;
		private var onOneComplete:Function;
		private var onError:Function;
		private var onProgress:Function;

		private var _errorText:String;

		private var _urls:Array;
		private var items:Array;
		private var curItem:LoaderItem;

		private var timer:STimer;
		private var time:int;
		private const TIME_OUT:int = 10;
		private var lastSize:uint;

		/**
		 * 最好不要重复利用此类的实例，因为重复调用loadOne或loadMany可能会打断正在
		 * 加载的过程。
		 */
		public function SLoader()
		{			
			
		}

		/**
		 * 停止加载，并清除已经加载的资源，相当于重置SLoader，
		 * 但不破坏已经加载资源的LoaderInfo。
		 */
		public function clear():void
		{
			if (curItem)
			{
				removeEvents(curItem);
				curItem.close();
				curItem = null;
			}

			items = null;
			_isLoadComple = false;
			_urls = null;
			_curLoadIndex = -1;
			_loaderList = null;
			time = 0;
			lastSize = 0;
			if(timer)
			{
				timer.dispose();
			}

			clearFunc();
		}

		private function clearFunc():void
		{
			this.params = null;
			this.onComplete = null;
			this.onOneComplete = null
			this.onProgress = null;
			this.onError = null;
		}

		public function unload(url:String, stop:Boolean=true):void
		{
			var ld:Object = loaderList[url];
			if (ld)
			{
				if (ld is Loader)
				{
					Loader(ld).unload();
					if (stop)
					{
						Loader(ld).unloadAndStop();
					}
				}
				delete loaderList[url];
			}
		}

		public function unloadAll(stop:Boolean=true):void
		{
			for (var url:String in loaderList)
			{
				var ld:Object = loaderList[url];
				if (ld)
				{
					if (ld is Loader)
					{
						Loader(ld).unload();
						if (stop)
						{
							Loader(ld).unloadAndStop();
						}
					}
					delete loaderList[url];
				}
			}
		}

		/**
		 * 加载单个资源，如果加载出错不会触发SLoaderEvent.complete事件
		 * @param url 资源的地址
		 * @param params onComplete、onError和onProgress的回调参数
		 * @param onComplete 资源加载完成后调用，仅一次有效
		 * @param onError 资源加载发生错误后调用，仅一次有效
		 * @param onProgress 资源加载过程中调用，仅一次有效
		 */
		public function loadOne(url:String, 
			params:Array = null, 
			onComplete:Function = null, 
			onError:Function = null, 
			onProgress:Function = null):void
		{
			if (url)
			{
				loadMany([url], params, onComplete, null, onError, onProgress);
			}
			else
			{
				Logger.Warning(this, "无法加载空url");
			}
		}

		/**
		 * 顺序加载多个资源，即使其中有几个资源没有加载成功，也会触发SLoaderEvent.complete事件
		 * @param urls 资源的地址
		 * @param params onAllComplete、onOneComplete、onError和onProgress的回调参数
		 * @param onAllComplete 所有资源加载完成后调用，仅一次有效
		 * @param onOneComplete 单个资源加载完成后调用，仅一次有效
		 * @param onError 单个资源加载错误后调用，仅一次有效
		 * @param onProgress 单个资源加载过程中调用，仅一次有效
		 */
		public function loadMany(urls:Array, 
			params:Array = null, 
			onAllComplete:Function = null, 
			onOneComplete:Function = null, 
			onError:Function = null, 
			onProgress:Function = null):void
		{
			if (!urls || urls.length == 0)
			{
				Logger.Warning(this, "无法加载空urls");
				return;
			}

			//先重置所有
			clear();

			_loaderList = new Dictionary();
			_urls = urls;
			this.params = params;
			this.onComplete = onAllComplete;
			this.onOneComplete = onOneComplete;
			this.onError = onError;
			this.onProgress = onProgress;

			items = [];
			for each (var u:String in urls)
			{
				items.push(new LoaderItem(u));
			}

			timer = new STimer(1000, 0, onTimer);
			timer.start();			
			loadNext();
		}
		
		/**
		 * 加载超时，就移到队列的末尾，等待下次加载
		 */
		private function onTimer():void
		{
			if(!curItem)
			{
				return;
			}
			
			if(lastSize != curItem.bytesLoaded)
			{
				lastSize = curItem.bytesLoaded;
				time = 0;				
				return;
			}
			
			if(++time >= TIME_OUT)
			{
				removeEvents(curItem);
				curItem.close();
				
				if(curItem.timeOutCount == 0)
				{
					curItem.timeOutCount = 1;
					var tmp:* = items[items.length - 1];
					items[items.length - 1] = curItem;
					items[_curLoadIndex--] = tmp;
				}
				loadNext();
			}
		}
		
		protected function loadNext():void
		{			
			time = 0;
			lastSize = 0;
			
			//全部加载完成
			if (_curLoadIndex == items.length - 1)
			{
				_isLoadComple = true;
				dispatchEvent(new SLoaderEvent(SLoaderEvent.COMPLETE, this));
				if (onComplete != null)
				{
					onComplete.apply(null, params);
				}

				items = null;
				//清除外部函数
				clearFunc();
				timer.dispose();
				return;
			}

			curItem = items[++_curLoadIndex];
			curItem.checkPolicyFile = checkPolicyFile;
			curItem.baseUrl = baseUrl;
			curItem.revisionList = revisionList;
			curItem.useCurrentDomain = useCurrentDomain;
			addEvents(curItem);
			curItem.load();
		}

		protected function addEvents(item:LoaderItem):void
		{
			item.addEventListener(SLoaderEvent.COMPLETE, onLoadCpt);
			item.addEventListener(SLoaderEvent.ERROR, onLoadError);
			item.addEventListener(SLoaderEvent.PROGRESS, onLoadProgress);
		}

		protected function removeEvents(item:LoaderItem):void
		{
			item.removeEventListener(SLoaderEvent.COMPLETE, onLoadCpt);
			item.removeEventListener(SLoaderEvent.ERROR, onLoadError);
			item.removeEventListener(SLoaderEvent.PROGRESS, onLoadProgress);
		}

		protected function onLoadCpt(e:SLoaderEvent):void
		{
			loaderList[curItem.url] = curItem.loader ? curItem.loader : curItem.urlLoader;
			if (onOneComplete != null)
			{
				onOneComplete.apply(null, params);
			}

			removeEvents(e.target as LoaderItem);
			loadNext();
		}

		protected function onLoadProgress(e:Event):void
		{
			if (hasEventListener(SLoaderEvent.PROGRESS))
			{
				dispatchEvent(new SLoaderEvent(SLoaderEvent.PROGRESS, this));
			}

			if (onProgress != null)
			{
				onProgress.apply(null, params);
			}
		}

		protected function onLoadError(e:Event):void
		{
			dispatchEvent(new SLoaderEvent(SLoaderEvent.ERROR, this));

			if (onError != null)
			{
				onError.apply(null, params);
			}

			removeEvents(e.target as LoaderItem);
			loadNext();
		}

		/**
		 * 要加载的资源总数
		 * @return
		 */
		public function get totalLoadCount():int
		{
			return urls ? urls.length : 0;
		}

		/**
		 * 当前加载到第几个资源，从0开始
		 * @default
		 */
		public function get curLoadIndex():int
		{
			return _curLoadIndex;
		}

		/**
		 * 当前加载资源自身加载的百分比数，如58.9
		 * @return
		 */
		public function get curLoadPercent():Number
		{
			return curItem ? curItem.curLoadPercent : 0;
		}

		/**
		 * 是否全部加载完成
		 * @return
		 */
		public function get isLoadComple():Boolean
		{
			return _isLoadComple;
		}

		public function get content():*
		{
			return curItem ? curItem.content : null;
		}

		/**
		 * 如果当前加载的是swf、图片等可视对象，则此值可用
		 * @return
		 */
		public function get contentDisplay():DisplayObject
		{
			return curItem ? curItem.contentDisplay : null;
		}

		/**
		 * 如果当前加载的是swf，则此值可用
		 * @return
		 */
		public function get contentMovieClip():MovieClip
		{
			return curItem ? curItem.contentMovieClip : null;
		}

		/**
		 * 如果当前加载的是图片，则此值可用
		 * @return
		 */
		public function get contentBitmap():Bitmap
		{
			return curItem ? curItem.contentBitmap : null;
		}

		/**
		 * 如果当前加载的是文本，则此值可用
		 * @return
		 */
		public function get contentText():String
		{
			return curItem ? curItem.contentText : null;
		}

		/**
		 * 如果当前加载的是二进制文件，则此值可用
		 * @return
		 */
		public function get contentBinary():ByteArray
		{
			return curItem ? curItem.contentBinary : null;
		}

		/**
		 * 如果当前加载资源的dataFormat==SLoaderDataFormat.DISPLAY，则此值可用
		 * @default
		 */
		public function get loader():Loader
		{
			return curItem ? curItem.loader : null;
		}

		/**
		 * 如果当前所加载资源的dataFormat==SLoaderDataFormat.DISPLAY，则此值可用
		 * @return
		 */
		public function get loaderInfo():LoaderInfo
		{
			return curItem ? curItem.loaderInfo : null;
		}

		/**
		 * 如果当前所加载资源的dataFormat是SLoaderDataFormat.TEXT、SLoaderDataFormat.BINARY,
		 * 则此值可用
		 * @return
		 */
		public function get urlLoader():URLLoader
		{
			return curItem ? curItem.urlLoader : null;
		}

		/**
		 * 当前加载的资源地址，如果所有资源都加载完成后，则是最后一个
		 * @default
		 */
		public function get url():String
		{
			return curItem ? curItem.url : null;
		}

		/**
		 * 所有要加载的资源地址
		 * @default
		 */
		public function get urls():Array
		{
			return _urls;
		}

		/**
		 * 所有已加载资源的Loader、URLLoader，
		 * [url]=Loader、URLLoader
		 * @return
		 */
		public function get loaderList():Dictionary
		{
			return _loaderList;
		}

		public function get errorText():String
		{
			return curItem ? curItem.errorText : "";
		}

		public function get useCurrentDomain():Boolean
		{
			return _useCurrentDomain;
		}

		public function set useCurrentDomain(value:Boolean):void
		{
			_useCurrentDomain = value;
		}

		public function get checkPolicyFile():Boolean
		{
			return _checkPolicyFile;
		}

		public function set checkPolicyFile(value:Boolean):void
		{
			_checkPolicyFile = value;
		}

	}
}